﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using UCS.XNA.CombatBiplanes.Synchronization;
using UCS.XNA.Utils;
using UCS.XNA.Utils.Animation;

namespace UCS.XNA.CombatBiplanes
{
    public class CombatBiplanesMenu : IFadeAnimatable
    {
        #region Constants

        private const string GAME_TITLE = "Combat Biplanes";
        private const string CLIENT_MENU_TEXT = "Cliente";
        private const string SERVER_MENU_TEXT = "Servidor";
        private const string SERVER_ADDRESS_MENU_TEXT = "Endereco do Servidor";
        private const string CONNECT_MENU_TEXT = "Conectar";
        private const string BACK_MENU_TEXT = "Voltar";
        private const string PLAYER_MENU_TEXT = "Jogador {0}";
        private const string START_MENU_TEXT = "Iniciar";
        private const string AWAITING_CONNECTION_MENU_TEXT = "Aguardando conexao...";
        private const string CONNECTED_MENU_TEXT = "Conectado";
        private const string CONNECTED_CURRENT_MENU_TEXT = "Conectado (Voce)";
        private const string EXIT_MENU_TEXT = "Sair";
        private const string BLANK_SERVER_ADDRESS_MESSAGE = "Favor informar o endereco do servidor!";

        #endregion

        #region Attributes and Properties

        private float _transparency;
        public float Transparency
        {
            get { return this._transparency; }
            set { this._transparency = value; }
        }

        #endregion

        #region Private Members

        private SpriteBatch _spriteBatch;
        private GraphicsDevice _graphics;
        private Menu _mainMenu;
        private Menu _clientMenu;
        private Menu _serverMenu;
        private TextBoxMenuItem _serverAddressMenuItem;
        private MenuItem _startMenuItem;
        private Dictionary<int, TextBoxMenuItem> _playerNumberItems;
        private Vector2 _menuTitlePosition;
        private Vector2 _gameTitlePosition;
        private string _menuTitle;
        private MessageWindow _messageWindow;
        private Rectangle _bounds;
        private MenuState _menuState;
        private readonly int _maximumPlayers;
        private bool _visible;
        private FadeAnimation _fadeAnimation;

        #endregion

        #region Events

        public event EventHandler<EventArgs> ExitClicked;

        public event EventHandler<EventArgs> StartClicked;

        public event EventHandler<StringEventArgs> ConnectClicked;

        public event EventHandler<EventArgs> CancelClicked;

        public event EventHandler<EventArgs> ServerClicked;

        public event EventHandler<EventArgs> ClientClicked;

        #endregion

        #region Constructors

        public CombatBiplanesMenu(Rectangle bounds, GraphicsDevice graphics, SpriteBatch spriteBatch, int maximumPlayers)
        {
            this._bounds = bounds;
            this._graphics = graphics;
            this._spriteBatch = spriteBatch;
            this._maximumPlayers = maximumPlayers;
            this._visible = true;
            this._transparency = 1;

            this.Initialize();
        }

        #endregion

        #region Private Methods

        private void Initialize()
        {
            this.InitializeGameState();
            this.InitializeMainMenu();
            this.InitializeClientMenu();
            this.InitializeServerMenu();
            this.InitializeGameTitlePosition();
            this.InitializeMenuTitle();
            this.InitializeMessageWindow();

            this._mainMenu.Show();
            this._clientMenu.Hide();
            this._serverMenu.Hide();
        }

        private void InitializeGameState()
        {
            this._menuState = MenuState.MainMenu;
        }

        private void InitializeMainMenu()
        {
            var bounds = new Rectangle();
            bounds.X = (int)(this._bounds.Width / 2 - (this._bounds.Width * 0.5f) / 2);
            bounds.Y = (int)(this._bounds.Height * 0.3);
            bounds.Width = (int)(this._bounds.Width * 0.5f);
            bounds.Height = (int)(this._bounds.Height * 0.2);
            this._mainMenu = new Menu(bounds);

            var clientMenuItem = new MenuItem(CLIENT_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            clientMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            clientMenuItem.Click += (sender, e) =>
            {
                if (this.ClientClicked != null)
                    this.ClientClicked(this, new EventArgs());

                this._mainMenu.Hide();
                this._clientMenu.Show();
                this.ChangeMenuState(MenuState.ClientMenu);
            };
            this._mainMenu.AddItem(clientMenuItem);

            var serverMenuItem = new MenuItem(SERVER_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            serverMenuItem.Border = new Border(ContentManager.Instance.MenuItemBorderTexture, ContentManager.Instance.MenuItemBorderTexture);
            serverMenuItem.Click += (sender, e) =>
            {
                if (this.ServerClicked != null)
                    this.ServerClicked(this, new EventArgs());

                this._mainMenu.Hide();
                this._serverMenu.Show();
                this.ChangeMenuState(MenuState.ServerMenu);
            };
            this._mainMenu.AddItem(serverMenuItem);

            var exitMenuItem = new MenuItem(EXIT_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            exitMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            exitMenuItem.Click += (sender, e) => this.RaiseGameExitedEvent();
            this._mainMenu.AddItem(exitMenuItem);
        }

        private void InitializeClientMenu()
        {
            var bounds = new Rectangle();
            bounds.X = (int)(this._bounds.Width / 2 - (this._bounds.Width * 0.7f) / 2);
            bounds.Y = (int)(this._bounds.Height * 0.3);
            bounds.Width = (int)(this._bounds.Width * 0.7f);
            bounds.Height = (int)(this._bounds.Height * 0.2);
            this._clientMenu = new Menu(bounds);

            this._serverAddressMenuItem = new TextBoxMenuItem(SERVER_ADDRESS_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            this._serverAddressMenuItem.TextBoxText = "localhost";
            this._serverAddressMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            this._clientMenu.AddItem(this._serverAddressMenuItem);

            var connectMenuItem = new MenuItem(CONNECT_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            connectMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            connectMenuItem.Click += (sender, e) =>
            {
                if (string.IsNullOrEmpty(this._serverAddressMenuItem.TextBoxText))
                    this.ShowMessage(BLANK_SERVER_ADDRESS_MESSAGE);
                else
                {
                    if (this.ConnectClicked != null)
                        this.ConnectClicked(this, new StringEventArgs(this._serverAddressMenuItem.TextBoxText));
                }
            };
            this._clientMenu.AddItem(connectMenuItem);

            var backMenuItem = new MenuItem(BACK_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            backMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            backMenuItem.Click += new EventHandler<EventArgs>((sender, e) =>
            {
                if (this.CancelClicked != null)
                    this.CancelClicked(this, new EventArgs());

                this._clientMenu.Hide();
                this._mainMenu.Show();
                this.ChangeMenuState(MenuState.MainMenu);
            });
            this._clientMenu.AddItem(backMenuItem);
        }

        private void InitializeServerMenu()
        {
            var bounds = new Rectangle();
            bounds.X = (int)(this._bounds.Width / 2 - (this._bounds.Width * 0.7f) / 2);
            bounds.Y = (int)(this._bounds.Height * 0.3);
            bounds.Width = (int)(this._bounds.Width * 0.7f);
            bounds.Height = (int)(this._bounds.Height * 0.45f);
            this._serverMenu = new Menu(bounds);

            this._playerNumberItems = new Dictionary<int, TextBoxMenuItem>();
            for (int i = 0; i < this._maximumPlayers; i++)
            {
                var playerMenuItem = new TextBoxMenuItem(string.Format(PLAYER_MENU_TEXT, i + 1), ContentManager.Instance.MenuItemFont);
                playerMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
                playerMenuItem.ReadOnly = true;
                playerMenuItem.TextBoxText = AWAITING_CONNECTION_MENU_TEXT;
                this._serverMenu.AddItem(playerMenuItem);
                this._playerNumberItems.Add(i + 1, playerMenuItem);
            }

            this._startMenuItem = new MenuItem(START_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            this._startMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            this._startMenuItem.Click += (sender, e) => this.RaiseGameStartedEvent();
            this._serverMenu.AddItem(this._startMenuItem);

            var backMenuItem = new MenuItem(BACK_MENU_TEXT, ContentManager.Instance.MenuItemFont);
            backMenuItem.Border = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            backMenuItem.Click += new EventHandler<EventArgs>((sender, e) =>
            {
                if (this.CancelClicked != null)
                    this.CancelClicked(this, new EventArgs());

                this._serverMenu.Hide();
                this._mainMenu.Show();
                this.ChangeMenuState(MenuState.MainMenu);
            });
            this._serverMenu.AddItem(backMenuItem);
        }

        private void InitializeGameTitlePosition()
        {
            var textSize = ContentManager.Instance.GameTitleFont.MeasureString(GAME_TITLE);
            this._gameTitlePosition = new Vector2(this._bounds.Width / 2 - textSize.X / 2, 0);
        }

        private void InitializeMenuTitle()
        {
            switch (this._menuState)
            {
                case MenuState.ClientMenu:
                    this._menuTitle = CLIENT_MENU_TEXT;
                    break;
                case MenuState.ServerMenu:
                    this._menuTitle = SERVER_MENU_TEXT;
                    break;
                default:
                    this._menuTitle = string.Empty;
                    break;
            }

            var textSize = ContentManager.Instance.MenuTitleFont.MeasureString(this._menuTitle);
            this._menuTitlePosition = new Vector2(this._bounds.Width / 2 - textSize.X / 2, 100);
        }

        private void InitializeMessageWindow()
        {
            Rectangle rect = new Rectangle();
            rect.X = 0;
            rect.Y = 0;
            rect.Width = this._bounds.Width;
            rect.Height = this._bounds.Height;
            this._messageWindow = new MessageWindow(rect, this._spriteBatch, this._graphics, ContentManager.Instance.MessageWindowFont);
            this._messageWindow.ButtonBorder = new Border(ContentManager.Instance.MenuItemCornerTexture, ContentManager.Instance.MenuItemBorderTexture);
            this._messageWindow.Closed += (sender, e) => this.UnPauseMenus();
        }

        private void DrawGameTitle()
        {
            this._spriteBatch.Begin();
            this._spriteBatch.DrawString(ContentManager.Instance.GameTitleFont, GAME_TITLE, this._gameTitlePosition, new Color(53, 54, 56) * this._transparency);
            this._spriteBatch.End();
        }

        private void DrawMenuTitle()
        {
            this._spriteBatch.Begin();
            this._spriteBatch.DrawString(ContentManager.Instance.MenuTitleFont, this._menuTitle, this._menuTitlePosition, Color.Black * this._transparency);
            this._spriteBatch.End();
        }

        private void PauseMenus()
        {
            this._mainMenu.Enabled = false;
            this._clientMenu.Enabled = false;
            this._serverMenu.Enabled = false;
        }

        private void UnPauseMenus()
        {
            this._mainMenu.Enabled = true;
            this._clientMenu.Enabled = true;
            this._serverMenu.Enabled = true;
        }

        private void ChangeMenuState(MenuState newGameState)
        {
            this._menuState = newGameState;
            this.InitializeMenuTitle();
        }

        private void RaiseGameExitedEvent()
        {
            if (this.ExitClicked != null)
                this.ExitClicked(this, new EventArgs());
        }

        private void RaiseGameStartedEvent()
        {
            if (this.StartClicked != null)
                this.StartClicked(this, new EventArgs());
        }

        #endregion

        #region Public Methods

        public void Hide()
        {
            this._fadeAnimation = new FadeOutAnimation(this, 0.05f);
            this._fadeAnimation.AnimationFinished += (sender, e) => { this._visible = false; };

            this._mainMenu.Hide();
            this._clientMenu.Hide();
            this._serverMenu.Hide();
        }

        public void Show()
        {
            this._visible = true;

            this._fadeAnimation = new FadeInAnimation(this, 0.05f);

            this._mainMenu.Show();
            this._clientMenu.Hide();
            this._serverMenu.Hide();
        }

        public void Update(GameTime gameTime, MouseState mouseState, KeyboardState keyboardState)
        {
            this._mainMenu.Update(gameTime, mouseState, keyboardState);
            this._clientMenu.Update(gameTime, mouseState, keyboardState);
            this._serverMenu.Update(gameTime, mouseState, keyboardState);
            this._messageWindow.Update(gameTime, mouseState, keyboardState);
            if (this._fadeAnimation != null) this._fadeAnimation.Update(gameTime);
        }

        public void Draw(GameTime gameTime)
        {
            if (this._visible)
            {
                this._mainMenu.Draw(gameTime);
                this._clientMenu.Draw(gameTime);
                this._serverMenu.Draw(gameTime);
                this.DrawGameTitle();
                this.DrawMenuTitle();
                this._messageWindow.Draw(gameTime);
            }
        }

        public void ShowMessage(string message)
        {
            this._messageWindow.Message = message;
            this.PauseMenus();
            this._messageWindow.Show();
        }

        public void ShowServerMenuAsClient()
        {
            this._clientMenu.Hide();

            this._serverMenu.Show();
            this._startMenuItem.Hide();
        }

        public void ChangePlayerMenuItemToConnected(int playerNumber, bool isCurrentPlayer)
        {
            if (isCurrentPlayer)
                this._playerNumberItems[playerNumber].TextBoxText = CONNECTED_CURRENT_MENU_TEXT;
            else
                this._playerNumberItems[playerNumber].TextBoxText = CONNECTED_MENU_TEXT;
        }

        #endregion
    }
}